#!/usr/bin/env just --justfile

set shell := ["bash", "-c"]

# The website is created using [Hugo](https://gohugo.io/).
# The theme is based on [Hextra](https://imfing.github.io/hextra/docs/).

# Build docs
build:
    hugo --gc --minify

# Serve docs
serve:
    hugo server

# Update version cargo files (e.g. 0.5.0-beta4)
update-version version:
    sed --in-place -e 's/^version = ".*/version = "{{version}}"/g' -e 's/^\(bbox-.* version =\) "[^"]*"/\1 "{{version}}"/g' -e 's/^\(bbox-routing-server.* version =\) "[^"]*"/\1 "0.1.0"/g' ../bbox-asset-server/Cargo.toml ../bbox-core/Cargo.toml ../bbox-feature-server/Cargo.toml ../bbox-frontend/Cargo.toml ../bbox-map-server/Cargo.toml ../bbox-processes-server/Cargo.toml ../bbox-server/Cargo.toml ../bbox-tile-server/Cargo.toml
    cd .. && cargo build

# Update version in downlad urls (e.g. 0.5.0-beta4)
update-version-doc version:
    V={{version}} VD=${V/-/.}; sed --in-place -e 's!download/v.*/!download/v{{version}}/!g' -e 's!sourcepole/\(bbox-.*\):.*!sourcepole/\1:v{{version}}!g' -e "s!_.*\(-.*.deb$\)!_$VD\1!g" content/docs/installation.md  content/docs/tile-server/installation.md

rustdoc:
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-core -- -Z unstable-options --output-format json
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-feature-server -- -Z unstable-options --output-format json
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-tile-server -- -Z unstable-options --output-format json
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-map-server -- -Z unstable-options --output-format json
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-asset-server -- -Z unstable-options --output-format json
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-processes-server -- -Z unstable-options --output-format json
    cargo +nightly-2024-09-23 rustdoc --lib -p bbox-routing-server -- -Z unstable-options --output-format json
    @# cat ../target/doc/bbox_tile_server.json | jq . >bbox-tile-server.json

gendoc: (genref "../target/doc/bbox_core.json" "content/docs/core/reference.md" "CoreServiceCfg" "Core Reference") (genref "../target/doc/bbox_tile_server.json" "content/docs/tile-server/reference.md" "TileServiceCfg" "Tile Server Reference") (genref "../target/doc/bbox_feature_server.json" "content/docs/feature-server/reference.md" "FeatureServiceCfg" "Feature Server Reference") (genref "../target/doc/bbox_map_server.json" "content/docs/map-server/reference.md" "MapServiceCfg" "Map Server Reference") (genref "../target/doc/bbox_asset_server.json" "content/docs/asset-server/reference.md" "AssetServiceCfg" "Asset Server Reference") (genref "../target/doc/bbox_processes_server.json" "content/docs/processes-server/reference.md" "ProcessesServiceCfg" "Processes Server Reference") (genref "../target/doc/bbox_routing_server.json" "content/docs/routing-server/reference.md" "RoutingServiceCfg" "Routing Server Reference")

# Extract configuration reference from rustdoc
refdoc: rustdoc gendoc

[private]
genref json md root title:
  #!/usr/bin/env python3
  import json
  import jmespath
  import sys
  import re

  def get(data, id):
      entry = jmespath.search("index.* | [?id=='%s']" % id, data)
      struct = jmespath.search("""index.* | [?id=='%s'] | [0].{name: name, attr: attrs[0], fieldtype: inner.struct_field.resolved_path.{name: name, id:id}, fieldrefs: inner.struct.kind.plain.fields, enums: inner.enum.variants, variant: inner.variant.kind.tuple, docs: docs}""" % id, data)
      return (entry, struct)

  def fname(struct):
      m = re.findall(r'rename = "(\w+)"', struct.get('attr') or '')
      name = m[0] if m else struct.get('name')
      return pretty_typename(name)

  def pretty_typename(name):
      mapping = {
          'NonZeroU16': 'u16',
          'std::path::PathBuf': 'Path',
      }
      for src, dst in mapping.items():
          name = name.replace(src, dst)
      name = name.removesuffix('ParamCfg')
      name = name.removesuffix('Cfg')
      name = re.sub(r"^.*::", "", name)
      return name

  def typedoc(data, root, toplevel):
      queue = [root]
      for idx, (level, id) in enumerate(queue):
          (entry, struct) = get(data, id)
          if struct:  # skip external or std
              if level > 0:
                  print("")
              title = toplevel if toplevel else fname(struct)
              print("#" * min(level+1, 4), end=' ')
              print(title)
              print("")
              if struct.get('docs'):
                print(struct.get('docs'))
              fieldrefs = struct['fieldrefs']
              if struct['variant']:
                  (ventry, vstruct) = get(data, struct['variant'][0])
                  (ventry, vstruct) = get(data, vstruct['fieldtype']['id'])
                  fieldrefs = vstruct['fieldrefs']
              if fieldrefs:
                  fieldtypesbf = []
                  for ref in fieldrefs:
                      (entry, struct) = get(data, ref)
                      struct = jmespath.search("""index.* | [?id=='%s'] | [0].{name: name, attr: attrs[0], fieldtype: inner.struct_field.resolved_path.{name: name, id:id}, fieldtypeargs: inner.struct_field.resolved_path.args.angle_bracketed.args[0].type.resolved_path.{name: name, id:id}, primitive: inner.struct_field.primitive, primitiveargs: inner.struct_field.resolved_path.args.angle_bracketed.args[0].type.primitive, variant: inner.variant.kind.tuple, docs: docs}""" % ref, data)
                      # TODO: handle {‘args’: [{‘type’: {‘tuple’: [{‘primitive’: ‘f64’}, {‘primitive’: ‘f64’}]}}]
                      fieldtype = {'name': struct['primitive'], 'id': None} if struct['primitive'] else struct['fieldtype']
                      fieldtypename = (fieldtype or {}).get('name')
                      fieldtypearg = {'name': struct['primitiveargs'], 'id': None} if struct['primitiveargs'] else struct['fieldtypeargs']
                      vec = '[]' if fieldtypename == 'Vec' else ''
                      if 'flatten' in (struct.get('attr') or ''):
                          fieldname = ''
                      else:
                          fieldname = f"`{fname(struct)}{vec}` "
                      typeid = (fieldtypearg or fieldtype).get('id')
                      queue.append((level+1, typeid))
                      typename = (fieldtypearg or fieldtype).get('name')
                      iscfg = 'Cfg' in typename
                      typename = pretty_typename(typename)
                      typemd = f"[{typename}](#{typename.lower()})" if iscfg else f"*{typename}*"
                      optional = ' (optional)' if fieldtypename == 'Option' or 'default' in (struct.get('attr') or '') else ''
                      docpart = f": {struct.get('docs')}" if struct.get('docs') else ''
                      docpart = docpart.replace("\n", "\n  ")
                      print(f"* {fieldname}{typemd}{optional}{docpart}")
                      #print(f" // {entry}")
              elif struct['enums']:
                  # Prepend
                  for id in reversed(struct['enums']):
                        queue.insert(idx+1, (level+1, id))
              if toplevel:
                  return queue[idx+1:]
      return []

  #with open('../target/doc/bbox_core.json') as f:
  #    coredata = json.load(f)
  with open('{{json}}') as f:
      data = json.load(f)
      id = jmespath.search("index.* | [?name=='%s'] | [0].id" % '{{root}}', data)
      with open('{{md}}', 'w') as sys.stdout:
          level1 = typedoc(data, (0, id), '{{title}}')
          for el in level1:
              typedoc(data, el, None)
